/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.diplomka.servicelayer.gamelogic.resolver;

import cz.fi.muni.diplomka.common.Army;
import cz.fi.muni.diplomka.common.Squad;
import cz.fi.muni.diplomka.common.enumeration.BuildingType;
import cz.fi.muni.diplomka.common.enumeration.ResourceType;
import cz.muni.fi.diplomka.servicelayer.dto.BuildingInCity;
import cz.muni.fi.diplomka.servicelayer.dto.CityArmy;
import cz.muni.fi.diplomka.servicelayer.dto.MarchingArmy;
import cz.muni.fi.diplomka.servicelayer.dto.Message;
import cz.muni.fi.diplomka.servicelayer.dto.Research;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.CityArmyManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.CityBuildingManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.CityManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.ResearchManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.CityResourcesManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.CityUnitsManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.MarchingArmyManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.MessageManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.WorldManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl.CityArmyManagerImpl;
import cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl.CityBuildingManagerImpl;
import cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl.CityManagerImpl;
import cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl.ResearchManagerImpl;
import cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl.CityResourcesManagerImpl;
import cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl.CityUnitsManagerImpl;
import cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl.MarchingArmyManagerImpl;
import cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl.MessageManagerImpl;
import cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl.WorldManagerImpl;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * Class used for checking of Armies arrivals
 *
 * @author Jakub Kutil
 */
public class BattleResolver {

    private static MarchingArmyManager marchingArmyManager = new MarchingArmyManagerImpl();
    private static CityResourcesManager cityResourcesManager = new CityResourcesManagerImpl();
    private static CityBuildingManager cityBuildingManager = new CityBuildingManagerImpl();
    private static CityUnitsManager cityUnitsManager = new CityUnitsManagerImpl();
    private static CityArmyManager cityArmyManager = new CityArmyManagerImpl();
    private static CityManager cityManager = new CityManagerImpl();
    private static MessageManager messageManager = new MessageManagerImpl();
    private static WorldManager worldManager = new WorldManagerImpl();
    private static ResearchManager cityResearchManager = new ResearchManagerImpl();
    private static Battler battler = new Battler();
    private static Random randomGenerator = new Random();

    public static void main(String[] args) throws InterruptedException {
        while (true) {
            for (MarchingArmy marchingArmy : marchingArmyManager.getAllMarchingArmies()) {
                Timestamp now = new Timestamp(System.currentTimeMillis());
                if (marchingArmy.getTimeOfArrival().before(now)) {

                    if (marchingArmy.isReturning()) {

                        CityArmy cityArmy = new CityArmy();
                        cityArmy.setArmy(marchingArmy.getArmy());
                        cityArmy.setCityId(marchingArmy.getCityIdFrom());
                        cityArmy.setGuard(false);
                        cityArmy.setName(marchingArmy.getName());
                        cityArmy.setOwnerCityId(marchingArmy.getOwnerCityId());

                        cityResourcesManager.increaseResource(marchingArmy.getCityIdFrom(), ResourceType.Food, marchingArmy.getStolenFood());
                        cityResourcesManager.increaseResource(marchingArmy.getCityIdFrom(), ResourceType.Gold, marchingArmy.getStolenGold());
                        cityResourcesManager.increaseResource(marchingArmy.getCityIdFrom(), ResourceType.Stone, marchingArmy.getStolenStone());
                        cityArmyManager.addArmy(cityArmy);
                        marchingArmyManager.deleteMarchingArmy(marchingArmy);

                        Message message = new Message();
                        message.setIdFrom(new Long(1));
                        message.setIdTo(cityManager.getOwnerOfCity(marchingArmy.getCityIdFrom()).getId());
                        message.setSubject("Return of army");
                        message.setBody("Your army " + marchingArmy.getName() + " has returned.");
                        message.setTime(now);
                        message.setUnread(true);

                        messageManager.addMessage(message);

                    } else {
                        int carry = 0;

                        for (Squad squad : marchingArmy.getArmy().getSquads()) {
                            int unitCarry = squad.getUnit().getCarry();
                            for (Research research : cityResearchManager.getAllFinishedResearchesOfPlayer(cityManager.getOwnerOfCity(marchingArmy.getOwnerCityId()).getId())) {
                                for (String stat : research.getEffects().keySet()) {
                                    if (stat.equals("carry")) {
                                        unitCarry += research.getEffects().get(stat);
                                    }
                                }
                            }
                            carry += unitCarry * squad.getCount();
                        }

                        marchingArmy.setReturning(true);

                        TravelDistanceResolver travelDistanceResolver = new TravelDistanceResolver();
                        double time = travelDistanceResolver.calculateDistance(marchingArmy.getCoordinates().getX_coordinate(), marchingArmy.getCoordinates().getY_coordinate(),
                                cityManager.getCityById(marchingArmy.getOwnerCityId()).getCoordinates().getX_coordinate(), cityManager.getCityById(marchingArmy.getOwnerCityId()).getCoordinates().getY_coordinate());

                        int bonusSpeed = 0;
                        for (Research research : cityResearchManager.getAllFinishedResearchesOfPlayer(cityManager.getOwnerOfCity(marchingArmy.getOwnerCityId()).getId())) {
                            for (String stat : research.getEffects().keySet()) {
                                if (stat.equals("speed")) {
                                    bonusSpeed += research.getEffects().get(stat);
                                }
                            }
                        }

                        double finalTime = new Double(0);

                        for (Squad squad : marchingArmy.getArmy().getSquads()) {
                            if (finalTime < (time / (squad.getUnit().getSpeed() + bonusSpeed))) {
                                finalTime = time / (squad.getUnit().getSpeed() + bonusSpeed);
                            }
                        }

                        java.util.Date date = new java.util.Date();
                        int t = (int) time;
                        Long finalTimeLong = new Long(Integer.toString(t));
                       
                        marchingArmy.setTimeOfArrival(new Timestamp((date.getTime() + 1000 * 60 * finalTimeLong)));



                        int bonusCarry = 0;
                        for (Research research : cityResearchManager.getAllFinishedResearchesOfPlayer(cityManager.getOwnerOfCity(marchingArmy.getOwnerCityId()).getId())) {
                            for (String stat : research.getEffects().keySet()) {
                                if (stat.equals("carry")) {
                                    bonusSpeed += research.getEffects().get(stat);
                                }
                            }
                        }
                        int loot = (carry + bonusCarry) * randomGenerator.nextInt(10) / 100;

                        switch (worldManager.getTileAtPosition(marchingArmy.getCoordinates().getX_coordinate(), marchingArmy.getCoordinates().getY_coordinate()).getType()) {

                            case village:
                                marchingArmy.setStolenFood(loot);
                                Message message = new Message();
                                message.setIdFrom(new Long(1));
                                message.setIdTo(cityManager.getOwnerOfCity(marchingArmy.getCityIdFrom()).getId());
                                message.setSubject("Raid on village");
                                message.setBody("Your army has raider village at [" + marchingArmy.getCoordinates().getX_coordinate() + "," + marchingArmy.getCoordinates().getX_coordinate()
                                        + "] and looted " + loot + "food.");
                                message.setTime(now);
                                message.setUnread(true);

                                marchingArmy.setStolenFood(loot);
                                marchingArmyManager.editMarchingArmy(marchingArmy);

                                messageManager.addMessage(message);

                                break;
                            case mountain:
                                marchingArmy.setStolenStone(loot);
                                message = new Message();
                                message.setIdFrom(new Long(1));
                                message.setIdTo(cityManager.getOwnerOfCity(marchingArmy.getCityIdFrom()).getId());
                                message.setSubject("Expedition to mountains");
                                message.setBody("Your army has has been at mountains at [" + marchingArmy.getCoordinates().getX_coordinate() + "," + marchingArmy.getCoordinates().getX_coordinate()
                                        + "] and mined " + loot + "stone.");
                                message.setTime(now);
                                message.setUnread(true);

                                marchingArmy.setStolenStone(loot);
                                marchingArmyManager.editMarchingArmy(marchingArmy);

                                messageManager.addMessage(message);
                                break;
                            case fortress:
                                marchingArmy.setStolenGold(loot);
                                message = new Message();
                                message.setIdFrom(new Long(1));
                                message.setIdTo(cityManager.getOwnerOfCity(marchingArmy.getCityIdFrom()).getId());
                                message.setSubject("Raid on fortress");
                                message.setBody("Your army has raider fortress at [" + marchingArmy.getCoordinates().getX_coordinate() + "," + marchingArmy.getCoordinates().getX_coordinate()
                                        + "] and looted " + loot + "gold.");
                                message.setTime(now);
                                message.setUnread(true);

                                marchingArmy.setStolenGold(loot);
                                marchingArmyManager.editMarchingArmy(marchingArmy);

                                messageManager.addMessage(message);
                                break;
                            case city:

                                switch (marchingArmy.getMovement()) {
                                    case Support:
                                        for (Squad squad : marchingArmy.getArmy().getSquads()) {
                                            cityUnitsManager.increaseUnitCount(cityManager.getCityAtPosition(marchingArmy.getCoordinates()).getId(), squad.getUnit().getName(), loot);
                                        }

                                        marchingArmyManager.deleteMarchingArmy(marchingArmy);
                                        break;
                                    case Reinforcement:
                                        CityArmy cityArmy = new CityArmy();
                                        cityArmy.setArmy(marchingArmy.getArmy());
                                        cityArmy.setCityId(marchingArmy.getCityIdFrom());
                                        cityArmy.setGuard(false);
                                        cityArmy.setName(marchingArmy.getName());
                                        cityArmy.setOwnerCityId(marchingArmy.getOwnerCityId());

                                        cityArmyManager.addArmy(cityArmy);
                                        marchingArmyManager.deleteMarchingArmy(marchingArmy);
                                        break;
                                    case Attack:
                                        Map<String, Army> armies = new HashMap<>();
                                        Long defenderCityId = cityManager.getCityAtPosition(marchingArmy.getCoordinates()).getId();
                                        cityArmy = cityArmyManager.getArmyOnCityWalls(cityManager.getCityAtPosition(marchingArmy.getCoordinates()).getId());
                                        if (!(cityArmy == null)) {
                                            armies = battler.battleArmies(marchingArmy.getArmy(), cityArmy.getArmy());
                                        } else {

                                            armies.put("attacker", marchingArmy.getArmy());
                                        }
                                        if (armies.get("attacker").getSquadAtPosition(armies.get("attacker").getSquads().size()).isActive()) {

                                            carry = 0;

                                            for (Squad squad : armies.get("attacker").getSquads()) {
                                                int unitCarry = squad.getUnit().getCarry();
                                                for (Research research : cityResearchManager.getAllFinishedResearchesOfPlayer(cityManager.getOwnerOfCity(marchingArmy.getOwnerCityId()).getId())) {
                                                    for (String stat : research.getEffects().keySet()) {
                                                        if (stat.equals("carry")) {
                                                            unitCarry += research.getEffects().get(stat);
                                                        }
                                                    }
                                                }
                                                carry += unitCarry * squad.getCount();
                                            }
                                            int walls = 1;
                                            for (BuildingInCity b : cityBuildingManager.getAllBuildingsInCityOfType(defenderCityId, BuildingType.Walls)) {
                                                walls = b.getLevel();
                                            }

                                            int lootFood = Math.min(((carry + bonusCarry) * randomGenerator.nextInt(15 - walls) / 300),
                                                    cityResourcesManager.getResourceCount(defenderCityId, ResourceType.Food));

                                            int lootGold = Math.min(((carry + bonusCarry) * randomGenerator.nextInt(15 - walls) / 300),
                                                    cityResourcesManager.getResourceCount(defenderCityId, ResourceType.Gold));

                                            int lootStone = Math.min(((carry + bonusCarry) * randomGenerator.nextInt(15 - walls) / 300),
                                                    cityResourcesManager.getResourceCount(defenderCityId, ResourceType.Stone));

                                            marchingArmy.setStolenFood(lootFood);
                                            marchingArmy.setStolenGold(lootGold);
                                            marchingArmy.setStolenStone(lootStone);

                                            message = new Message();
                                            message.setIdFrom(new Long(1));
                                            message.setIdTo(cityManager.getOwnerOfCity(marchingArmy.getCityIdFrom()).getId());
                                            message.setSubject("Skirmish at " + cityManager.getCityAtPosition(marchingArmy.getCoordinates()).getName());
                                            message.setBody("We have won at " + cityManager.getCityAtPosition(marchingArmy.getCoordinates()).getName()
                                                    + " and looted " + lootFood + " food, " + lootGold + " gold and " + lootStone + " stone.");
                                            message.setTime(now);
                                            message.setUnread(true);

                                            messageManager.addMessage(message);

                                            message = new Message();
                                            message.setIdFrom(new Long(1));
                                            message.setIdTo(defenderCityId);
                                            message.setSubject("Skirmish at " + cityManager.getCityAtPosition(marchingArmy.getCoordinates()));
                                            message.setBody("We have suffered defeat at " + cityManager.getCityAtPosition(marchingArmy.getCoordinates())
                                                    + " against army from " + cityManager.getCityById(marchingArmy.getCityIdFrom())
                                                    + " and lost " + lootFood + " food, " + lootGold + " gold and " + lootStone + " stone.");

                                            message.setTime(now);
                                            message.setUnread(true);

                                            messageManager.addMessage(message);

                                        } else {
                                            message = new Message();
                                            message.setIdFrom(new Long(1));
                                            message.setIdTo(cityManager.getOwnerOfCity(marchingArmy.getCityIdFrom()).getId());
                                            message.setSubject("Skirmish at " + cityManager.getCityAtPosition(marchingArmy.getCoordinates()));
                                            message.setBody("Our army has been defeated at " + cityManager.getCityAtPosition(marchingArmy.getCoordinates()) + ".");
                                            message.setTime(now);
                                            message.setUnread(true);

                                            messageManager.addMessage(message);

                                            message = new Message();
                                            message.setIdFrom(new Long(1));
                                            message.setIdTo(defenderCityId);
                                            message.setSubject("Skirmish at " + cityManager.getCityAtPosition(marchingArmy.getCoordinates()));
                                            message.setBody("Our army has defended succesfully against army from " + cityManager.getCityById(marchingArmy.getCityIdFrom()) + ".");
                                            message.setTime(now);
                                            message.setUnread(true);

                                            messageManager.addMessage(message);
                                        }

                                        marchingArmy.setArmy(armies.get("attacker"));
                                        if (!(cityArmy == null)) {
                                            cityArmy.setArmy(armies.get("defender"));
                                            cityArmyManager.editArmy(cityArmy);
                                        }
                                        marchingArmyManager.editMarchingArmy(marchingArmy);

                                        break;
                                }
                                break;
                        }
                    }
                }
            }
            Thread.sleep(1000);
        }
    }
}
